package com.sweet.cls;

import lombok.extern.slf4j.Slf4j;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * 反射工具
 *
 * @author limi
 * @version 2015-10-30
 */
@Slf4j
public final class ReflectionUtil {
    private ReflectionUtil() {
    }

    /**
     * 检查并使构造函数可访问
     */
    public static void makeAccessible(Constructor<?> ctor) {
        // 若父构造函数不是public，或自身定义的构造函数不是public，并且构造函数本身不可访问
        if ((!Modifier.isPublic(ctor.getModifiers()) || !Modifier.isPublic(ctor.getDeclaringClass().getModifiers())) && !ctor.isAccessible()) {
            ctor.setAccessible(true);
        }
    }

    /**
     * 调用方法
     *
     * @param obj
     * @param method
     * @param args
     * @return
     */
    public static Object invokeMethod(Object obj, Method method, Object... args) {
        Object result;
        try {
            // 在调用 invoke 方法前，设置 Accessiable 属性为 true。注意：方法的 Accessiable
            // 属性并非它的字面意思“可访问的”（为 true 才能访问，为 false 就不能访问了），它真正的作用是为了取消 Java
            // 反射提供的类型安全性检测。在大量反射调用的过程中，这样做可以提高 20 倍以上的性能（据相关人事透露）。
            method.setAccessible(true);// 取消类型安全检测（可提高反射性能）
            result = method.invoke(obj, args);
        } catch (Exception e) {
            log.error("invoke method failure!", e);
            throw new RuntimeException(e);
        }
        return result;
    }

    /**
     * 设置成员变量值
     *
     * @param obj
     * @param field
     * @param value
     */
    public static void setField(Object obj, Field field, Object value) {
        try {
            field.setAccessible(true);// 取消类型安全检测（可提高反射性能）
            field.set(obj, value);
        } catch (Exception e) {
            log.error("set field failure!", e);
            throw new RuntimeException(e);
        }
    }
}
